from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from django.db.models import Sum
from django.utils import timezone
from django.core.cache import cache
from decimal import Decimal
import json
import re
import random
from .models import CarbonFootprint, CarbonFactor
from users.models import UserProfile
from datetime import timedelta, datetime
import os
import urllib.request
import urllib.parse
import time
import hmac
import hashlib
import base64
from urllib.parse import urlparse
from email.utils import formatdate
from types import SimpleNamespace
import hashlib
from django.core.cache import cache
import hmac
import base64
from email.utils import formatdate
import difflib


DEFAULT_FACTORS = {
    '牛肉': {'category': '餐饮', 'unit': 'kg', 'emission_per_unit': Decimal('27.0')},
    '红肉': {'category': '餐饮', 'unit': 'kg', 'emission_per_unit': Decimal('27.0')},
    '猪肉': {'category': '餐饮', 'unit': 'kg', 'emission_per_unit': Decimal('12.0')},
    '禽肉': {'category': '餐饮', 'unit': 'kg', 'emission_per_unit': Decimal('6.0')},
    '咖啡奶茶': {'category': '餐饮', 'unit': '杯', 'emission_per_unit': Decimal('0.4')},
    '地铁': {'category': '交通', 'unit': 'km', 'emission_per_unit': Decimal('0.05')},
    '公交': {'category': '交通', 'unit': 'km', 'emission_per_unit': Decimal('0.10')},
    '短袖': {'category': '购物', 'unit': '件', 'emission_per_unit': Decimal('2.0')},
    'T恤': {'category': '购物', 'unit': '件', 'emission_per_unit': Decimal('2.0')},
    '上衣': {'category': '购物', 'unit': '件', 'emission_per_unit': Decimal('2.0')},
    '骑行': {'category': '交通', 'unit': 'km', 'emission_per_unit': Decimal('0.01')},
    '驾车': {'category': '交通', 'unit': 'km', 'emission_per_unit': Decimal('0.21')},
    '空调': {'category': '能源', 'unit': 'h', 'emission_per_unit': Decimal('0.50')},
}

DEFAULT_UNIT_QTY = {
    'kg': Decimal('0.35'),
    'km': Decimal('5'),
    'h': Decimal('1'),
    '杯': Decimal('1'),
    '件': Decimal('1'),
    'm3': Decimal('1')
}

ITEM_SYNONYMS = {
    '羊肉': '红肉',
    '羊杂汤': '红肉',
    '涮羊肉': '红肉',
    '烤羊肉': '红肉',
    '烤羊排': '红肉',
    '牛排': '牛肉',
    '牛肉面': '红肉',
    '红烧牛肉': '红肉',
    '丸子汤': '猪肉',
    '肉丸汤': '猪肉',
    '贡丸汤': '猪肉',
    '鸡肉': '禽肉',
    '鸡腿': '禽肉',
    '鸡排': '禽肉',
    '鸡翅': '禽肉',
    '鸡胸': '禽肉',
    '鸡块': '禽肉',
    '鸭肉': '禽肉',
    '鹅肉': '禽肉',
    '火鸡': '禽肉',
    '白肉': '禽肉',
    '肌肉': '禽肉',
    '步行': '骑行',
    '走路': '骑行',
    '走了': '骑行',
    '走': '骑行',
    '跑步': '骑行',
    '慢跑': '骑行',
    '开车': '驾车',
    '自驾': '驾车',
    '打车': '驾车',
    '出租车': '驾车',
    '网约车': '驾车',
    '滴滴': '驾车',
    '共享单车': '骑行',
    '骑车': '骑行',
    '坐地铁': '地铁',
    '坐公交': '公交',
    '乘飞机': '飞机',
    '坐飞机': '飞机',
    '高铁': '飞机',
    '火车': '飞机',
    '可乐': '咖啡奶茶',
    '奶茶': '咖啡奶茶',
    '咖啡': '咖啡奶茶',
    '饮料': '咖啡奶茶',
    '啤酒': '咖啡奶茶',
    '米饭': '主食',
    '面条': '主食',
    '水果': '蔬菜',
    '蔬菜': '蔬菜',
}

LEARNED_SYNONYMS = {}

def _infer_category(item, unit):
    name = str(item or '')
    u = str(unit or '')
    if name in DEFAULT_FACTORS:
        return DEFAULT_FACTORS[name]['category']
    if name in ITEM_SYNONYMS:
        canon = ITEM_SYNONYMS[name]
        if canon in DEFAULT_FACTORS:
            return DEFAULT_FACTORS[canon]['category']
    if u in ['km']:
        return '交通'
    if u in ['件']:
        return '购物'
    if u in ['h', 'm3']:
        return '能源'
    if u in ['kg']:
        if re.search(r"[肉菜饭面水果蔬菜]", name):
            return '餐饮'
    if u in ['杯', '份', '个']:
        return '餐饮'
    if re.search(r"(汉堡|披萨|炸鸡|薯条|热狗|三明治)", name):
        return '餐饮'
    return ''

def _find_default_factor(text, unit):
    txt = (text or '')
    for syn, canon in LEARNED_SYNONYMS.items():
        if syn in txt:
            cfg = DEFAULT_FACTORS.get(canon)
            if cfg and (unit is None or unit == cfg['unit']):
                return SimpleNamespace(item_name=canon, category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
    for key, cfg in DEFAULT_FACTORS.items():
        if key in txt and (unit is None or unit == cfg['unit']):
            return SimpleNamespace(item_name=key, category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
    for syn, canon in ITEM_SYNONYMS.items():
        if syn in txt:
            cfg = DEFAULT_FACTORS.get(canon)
            if cfg and (unit is None or unit == cfg['unit']):
                return SimpleNamespace(item_name=canon, category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
    tokens = sorted(re.findall(r"[\u4e00-\u9fa5]+", txt), key=lambda x: len(x), reverse=True)
    if tokens:
        names = list(DEFAULT_FACTORS.keys()) + list(CarbonFactor.objects.values_list('item_name', flat=True))
        best_name = None
        best_ratio = 0.0
        token = tokens[0]
        if ('羊' in token) and DEFAULT_FACTORS.get('红肉'):
            cfg = DEFAULT_FACTORS['红肉']
            if unit is None or unit == cfg['unit']:
                LEARNED_SYNONYMS[token] = '红肉'
                return SimpleNamespace(item_name='红肉', category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
        if ('牛' in token) and (DEFAULT_FACTORS.get('牛肉') or DEFAULT_FACTORS.get('红肉')):
            name = '牛肉' if DEFAULT_FACTORS.get('牛肉') else '红肉'
            cfg = DEFAULT_FACTORS[name]
            if unit is None or unit == cfg['unit']:
                LEARNED_SYNONYMS[token] = name
                return SimpleNamespace(item_name=name, category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
        if ('猪' in token) and DEFAULT_FACTORS.get('猪肉'):
            cfg = DEFAULT_FACTORS['猪肉']
            if unit is None or unit == cfg['unit']:
                LEARNED_SYNONYMS[token] = '猪肉'
                return SimpleNamespace(item_name='猪肉', category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
        for n in names:
            r = difflib.SequenceMatcher(None, token, n).ratio()
            if r > best_ratio:
                best_ratio = r
                best_name = n
        if best_name and best_ratio >= 0.6:
            cfg = DEFAULT_FACTORS.get(best_name)
            if cfg and (unit is None or unit == cfg['unit']):
                LEARNED_SYNONYMS[token] = best_name
                return SimpleNamespace(item_name=best_name, category=cfg['category'], unit=cfg['unit'], emission_per_unit=cfg['emission_per_unit'])
            fac = CarbonFactor.objects.filter(item_name=best_name).first()
            if fac and (unit is None or unit == fac.unit):
                LEARNED_SYNONYMS[token] = best_name
                return fac
    return None

def _paginate(queryset, page, page_size):
    try:
        page = int(page or 1)
        page_size = int(page_size or 10)
    except Exception:
        page, page_size = 1, 10
    total = queryset.count()
    start = (page - 1) * page_size
    end = start + page_size
    return queryset[start:end], {'page': page, 'page_size': page_size, 'total': total}


def _parse_text(text):
    candidates = CarbonFactor.objects.all()
    chosen = None
    for f in candidates:
        if f.item_name.lower() in text.lower():
            chosen = f
            break
    if not chosen:
        chosen = _find_default_factor(text, None)
        if not chosen:
            return None, None, None
    pattern = r"([0-9]+(?:\.[0-9]+)?)\s*" + re.escape(chosen.unit)
    m = re.search(pattern, text)
    qty = None
    if m:
        qty = Decimal(m.group(1))
    else:
        m2 = re.search(r"([0-9]+(?:\.[0-9]+)?)", text)
        qty = Decimal(m2.group(1)) if m2 else None
    if qty is None:
        qty = DEFAULT_UNIT_QTY.get(chosen.unit)
        if qty is None:
            return None, None, None
    # 单位归一：如果文本中出现中文单位且与库单位不同，按比例换算
    try:
        txt = (text or '')
        if chosen.unit == 'kg':
            if re.search(r'斤', txt):
                qty = (Decimal(str(qty)) * Decimal('0.5'))
            elif re.search(r'克', txt) or re.search(r'(?i)(?<![k])g', txt):
                qty = (Decimal(str(qty)) / Decimal('1000'))
        if chosen.unit == 'km':
            if re.search(r'米', txt) or re.search(r'(?i)(?<!k)m\b', txt):
                if not re.search(r'(?i)km', txt):
                    qty = (Decimal(str(qty)) / Decimal('1000'))
    except Exception:
        pass
    emission = (chosen.emission_per_unit * qty).quantize(Decimal('0.01'))
    quantity_str = f"{qty}{chosen.unit}"
    return chosen, emission, quantity_str

def _parse_text_ai(text):
    try:
        provider = (os.environ.get('AI_PROVIDER') or '').upper()
        if provider in ['', 'SPARKLITE']:
            appid = os.environ.get('SPARK_APPID') or os.environ.get('SPARK_APP_ID')
            api_key = os.environ.get('SPARK_API_KEY')
            api_secret = os.environ.get('SPARK_API_SECRET')
            spark_base = os.environ.get('SPARK_BASE_URL') or 'https://spark-api.xf-yun.com'
            spark_path = os.environ.get('SPARK_PATH') or '/v3.1/chat'
            domain = os.environ.get('SPARK_DOMAIN') or 'lite'
            if not (appid and api_key and api_secret):
                return None, None, None
            h = hashlib.md5((text or '').lower().encode('utf-8')).hexdigest()
            ck = f"carbon:ai:{h}"
            cached = cache.get(ck)
            if cached:
                arr = cached if isinstance(cached, list) else []
            else:
                url = spark_base.rstrip('/') + spark_path
                parsed_url = urllib.parse.urlparse(url)
                host = parsed_url.netloc
                date = formatdate(usegmt=True)
                request_line = f"POST {parsed_url.path} HTTP/1.1"
                signature_origin = f"host: {host}\ndate: {date}\n{request_line}"
                signature_sha = hmac.new(api_secret.encode('utf-8'), signature_origin.encode('utf-8'), hashlib.sha256).digest()
                signature = base64.b64encode(signature_sha).decode('utf-8')
                authorization = f'api_key="{api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature}"'
                payload = {
                    "header": {"app_id": appid, "uid": "parse"},
                    "parameter": {"chat": {"domain": domain, "temperature": 0.1, "max_tokens": 800}},
                    "payload": {"message": {"text": [
                        {"role": "system", "content": "仅按要求返回JSON"},
                        {"role": "user", "content": build_calculation_prompt(text)}
                    ]}}
                }
                body = json.dumps(payload).encode('utf-8')
                req = urllib.request.Request(url, data=body)
                req.add_header('Content-Type', 'application/json')
                req.add_header('Host', host)
                req.add_header('Date', date)
                req.add_header('Authorization', authorization)
                try:
                    with urllib.request.urlopen(req, timeout=10) as resp:
                        raw = resp.read().decode('utf-8')
                        data = json.loads(raw)
                    result_text = ""
                    segs = (((data.get('payload') or {}).get('choices') or {}).get('text') or [])
                    for s in segs:
                        result_text += s.get('content') or ''
                    parsed = parse_llm_json(result_text)
                except Exception:
                    open_url = 'https://spark-api-open.xf-yun.com/v1/chat/completions'
                    open_pwd = os.environ.get('SPARK_API_PASSWORD') or ''
                    headers = {
                        'Content-Type': 'application/json',
                        'Authorization': f'Bearer {open_pwd}'
                    }
                    data_open = {
                        'model': domain,
                        'messages': [
                            {"role": "system", "content": "仅按要求返回JSON"},
                            {"role": "user", "content": build_calculation_prompt(text)}
                        ],
                        'temperature': 0.2,
                        'max_tokens': 800
                    }
                    req_open = urllib.request.Request(open_url, data=json.dumps(data_open).encode('utf-8'), headers=headers)
                    with urllib.request.urlopen(req_open, timeout=10) as resp_open:
                        raw_open = resp_open.read().decode('utf-8')
                        data_open_resp = json.loads(raw_open)
                    content_open = ((data_open_resp.get('choices') or [{}])[0].get('message') or {}).get('content') or '[]'
                    parsed = parse_llm_json(content_open)
                cache.set(ck, parsed, 900)
                arr = parsed if isinstance(parsed, list) else []
            if not arr:
                return None, None, None
            obj = arr[0]
            item = str(obj.get('item') or '').strip()
            unit = str(obj.get('unit') or '').strip()
            qty = obj.get('amount')
            factor_v = obj.get('factor')
            total_v = obj.get('total_co2')
            category = _infer_category(item, unit)
        else:
            key = os.environ.get('DEEPSEEK_API_KEY') or os.environ.get('AI_API_KEY')
            base = os.environ.get('DEEPSEEK_BASE_URL') or os.environ.get('AI_BASE_URL') or 'https://api.deepseek.com'
            model = os.environ.get('DEEPSEEK_MODEL') or os.environ.get('AI_MODEL') or 'deepseek-chat'
            if not key:
                return None, None, None
            h = hashlib.md5((text or '').lower().encode('utf-8')).hexdigest()
            ck = f"carbon:ai:{h}"
            cached = cache.get(ck)
            if cached:
                item = (cached.get('item') or '').strip()
                unit = (cached.get('unit') or '').strip()
                qty = cached.get('quantity')
                category = (cached.get('category') or '').strip()
            else:
                endpoint = base.rstrip('/') + '/v1/chat/completions'
                payload = {
                    "model": model,
                    "messages": [
                        {"role": "system", "content": "只按用户提示返回 JSON"},
                        {"role": "user", "content": build_calculation_prompt(text)}
                    ]
                }
                body = json.dumps(payload).encode('utf-8')
                req = urllib.request.Request(endpoint, data=body)
                req.add_header('Content-Type', 'application/json')
                req.add_header('Authorization', f'Bearer {key}')
                with urllib.request.urlopen(req, timeout=8) as resp:
                    raw = resp.read().decode('utf-8')
                    data = json.loads(raw)
                content = ((data.get('choices') or [{}])[0].get('message') or {}).get('content')
                if not content:
                    return None, None, None
                parsed = parse_llm_json(content)
                cache.set(ck, parsed, 3600)
                arr = parsed if isinstance(parsed, list) else []
                obj = (arr[0] if arr else {})
                item = str(obj.get('item') or '').strip()
                unit = str(obj.get('unit') or '').strip()
                qty = obj.get('amount')
                factor_v = obj.get('factor')
                total_v = obj.get('total_co2')
                category = _infer_category(item, unit)
        if not item or not unit:
            return None, None, None
        if qty is None:
            qty = DEFAULT_UNIT_QTY.get(unit)
            if qty is None:
                return None, None, None
        def _normalize(u, q):
            ul = (u or '').lower().strip()
            qd = Decimal(str(q))
            if ul in ['g', '克']:
                return 'kg', (qd / Decimal('1000'))
            if ul in ['斤']:
                return 'kg', (qd * Decimal('0.5'))
            if ul in ['公里', '千米', 'km']:
                return 'km', qd
            if ul in ['米', 'm']:
                return 'km', (qd / Decimal('1000'))
            if ul in ['杯']:
                return '杯', qd
            if ul in ['份']:
                return '份', qd
            if ul in ['个']:
                return '个', qd
            if ul in ['瓶']:
                return '杯', qd
            if ul in ['件']:
                return '件', qd
            if ul in ['立方米', 'm3']:
                return 'm3', qd
            return u, qd
        norm_unit, qty_dec = _normalize(unit, qty)
        if factor_v is None:
            return None, None, None
        emission = Decimal(str(total_v)) if total_v is not None else (Decimal(str(factor_v)) * qty_dec)
        emission = emission.quantize(Decimal('0.01'))
        factor_ns = SimpleNamespace(item_name=item or '未命名', category=category or '未分类', unit=norm_unit, emission_per_unit=Decimal(str(factor_v)))
        quantity_str = f"{qty_dec}{norm_unit}"
        return factor_ns, emission, quantity_str
    except Exception:
        return None, None, None


@csrf_exempt
@require_http_methods(["GET", "POST"])
def footprints_view(request):
    if request.method == "GET":
        try:
            if not request.user.is_authenticated:
                return JsonResponse({'success': False, 'message': '未登录'}, status=401)
            qs = CarbonFootprint.objects.filter(user=request.user)
            category = request.GET.get('category')
            start_date = request.GET.get('start_date')
            end_date = request.GET.get('end_date')
            if category:
                qs = qs.filter(category=category)
            if start_date:
                qs = qs.filter(record_date__gte=start_date)
            if end_date:
                qs = qs.filter(record_date__lte=end_date)
            page = request.GET.get('page')
            page_size = request.GET.get('page_size')
            page_qs, meta = _paginate(qs.order_by('-record_date', '-footprint_id'), page, page_size)
            data = [{
                'footprint_id': f.footprint_id,
                'category': f.category,
                'quantity': f.quantity,
                'carbon_emission': str(f.carbon_emission),
                'record_date': f.record_date.strftime('%Y-%m-%d')
            } for f in page_qs]
            return JsonResponse({'success': True, 'data': {'items': data, 'meta': meta}})
        except Exception as e:
            return JsonResponse({'success': False, 'message': str(e)}, status=500)
    if request.method == "POST":
        try:
            if not request.user.is_authenticated:
                return JsonResponse({'success': False, 'message': '未登录'}, status=401)
            payload = json.loads(request.body or '{}')
            text = payload.get('text')
            record_date = payload.get('record_date')
            override_category = str(payload.get('category') or '').strip()
            if not text:
                return JsonResponse({'success': False, 'message': '缺少text'}, status=400)
            factor, emission, quantity_str = _parse_text_ai(text)
            if not factor:
                factor, emission, quantity_str = _parse_text(text)
            if not factor:
                return JsonResponse({'success': False, 'message': '无法解析文本'}, status=400)
            if record_date:
                try:
                    dt_date = datetime.strptime(record_date, '%Y-%m-%d').date()
                except Exception:
                    dt_date = timezone.now().date()
            else:
                dt_date = timezone.now().date()
            final_category = factor.category or '未分类'
            if override_category:
                allowed_categories = [c[0] for c in CarbonFactor.CATEGORY_CHOICES]
                if override_category in allowed_categories:
                    final_category = override_category
            fp = CarbonFootprint.objects.create(
                user=request.user,
                category=final_category,
                quantity=quantity_str,
                carbon_emission=emission,
                record_date=dt_date
            )
            try:
                from rewards.utils import award_points
                award_points(request.user, 3, 'carbon_record', fp.footprint_id)
            except Exception:
                pass
            mqty = re.search(r"([0-9]+(?:\.[0-9]+)?)", quantity_str or '')
            std_amount = mqty.group(1) if mqty else ''
            std_unit = ''.join(re.findall(r"[a-zA-Z\u4e00-\u9fa5]+", quantity_str or ''))
            bf_map = {
                '地铁': Decimal('0.21'),
                '公交': Decimal('0.21'),
                '骑行': Decimal('0.21'),
                '步行': Decimal('0.21')
            }
            item_name_std = getattr(factor, 'item_name', '') or ''
            unit_std = std_unit or (factor.unit if hasattr(factor, 'unit') else '')
            amt_dec = Decimal(str(std_amount or '0') or '0') if str(std_amount or '').strip() != '' else Decimal('0')
            actual_fac = Decimal(str(getattr(factor, 'emission_per_unit', '0') or '0'))
            base_fac = bf_map.get(item_name_std, None)
            reduced = Decimal('0.00')
            if base_fac is not None:
                try:
                    reduced = ((base_fac - actual_fac) * amt_dec).quantize(Decimal('0.01'))
                except Exception:
                    reduced = Decimal('0.00')
            std = {
                'item': item_name_std,
                'unit': unit_std,
                'amount': str(std_amount or ''),
                'factor': str(getattr(factor, 'emission_per_unit', '')),
                'total_co2': str(emission),
                'category': getattr(factor, 'category', '') or '',
                'baseline_factor': str(base_fac) if base_fac is not None else '',
                'reduced_co2': str(reduced)
            }
            try:
                cache.set(f"carbon:std:{fp.footprint_id}", std, 90*24*3600)
            except Exception:
                pass
            return JsonResponse({'success': True, 'message': '创建成功', 'data': {
                'footprint_id': fp.footprint_id,
                'category': fp.category,
                'quantity': fp.quantity,
                'carbon_emission': str(fp.carbon_emission),
                'record_date': fp.record_date.strftime('%Y-%m-%d'),
                'standardized': std
            }}, status=201)
        except json.JSONDecodeError:
            return JsonResponse({'success': False, 'message': '无效的JSON'}, status=400)
        except Exception as e:
            return JsonResponse({'success': False, 'message': str(e)}, status=500)


@csrf_exempt
@require_http_methods(["GET"])
@login_required
def statistics_view(request):
    try:
        start_date = request.GET.get('start_date')
        end_date = request.GET.get('end_date')
        group_by = request.GET.get('group_by', 'category')
        include_reduced = str(request.GET.get('include_reduced') or '').lower() in ['1','true','yes']
        qs = CarbonFootprint.objects.filter(user=request.user)
        if start_date:
            qs = qs.filter(record_date__gte=start_date)
        if end_date:
            qs = qs.filter(record_date__lte=end_date)
        total = qs.aggregate(total_emission=Sum('carbon_emission'))['total_emission'] or Decimal('0')
        total_reduced = Decimal('0')
        if include_reduced:
            for fp in qs:
                std = cache.get(f"carbon:std:{fp.footprint_id}") or {}
                rr = Decimal(str(std.get('reduced_co2') or '0') or '0')
                total_reduced += rr
        if group_by == 'category':
            agg = qs.values('category').annotate(sum_emission=Sum('carbon_emission')).order_by('-sum_emission')
            groups = [{'category': a['category'], 'carbon_emission': str(a['sum_emission'] or Decimal('0'))} for a in agg]
            out = {'total': str(total), 'by_category': groups}
            if include_reduced:
                red_map = {}
                for fp in qs:
                    std = cache.get(f"carbon:std:{fp.footprint_id}") or {}
                    rr = Decimal(str(std.get('reduced_co2') or '0') or '0')
                    cat = fp.category or '其他'
                    red_map[cat] = red_map.get(cat, Decimal('0')) + rr
                out['total_reduced'] = str(total_reduced.quantize(Decimal('0.01')))
                out['by_category_reduced'] = [{'category': k, 'reduced_co2': str(v.quantize(Decimal('0.01')))} for k, v in sorted(red_map.items(), key=lambda x: x[1], reverse=True)]
            return JsonResponse({'success': True, 'data': out})
        if group_by == 'month':
            items = {}
            for fp in qs:
                key = fp.record_date.strftime('%Y-%m')
                items[key] = (items.get(key, Decimal('0')) + fp.carbon_emission)
            by_month = [{'month': k, 'carbon_emission': str(v)} for k, v in sorted(items.items())]
            out = {'total': str(total), 'by_month': by_month}
            if include_reduced:
                red_items = {}
                for fp in qs:
                    k = fp.record_date.strftime('%Y-%m')
                    std = cache.get(f"carbon:std:{fp.footprint_id}") or {}
                    rr = Decimal(str(std.get('reduced_co2') or '0') or '0')
                    red_items[k] = red_items.get(k, Decimal('0')) + rr
                out['total_reduced'] = str(total_reduced.quantize(Decimal('0.01')))
                out['by_month_reduced'] = [{'month': k, 'reduced_co2': str(v.quantize(Decimal('0.01')))} for k, v in sorted(red_items.items())]
            return JsonResponse({'success': True, 'data': out})
        return JsonResponse({'success': False, 'message': '不支持的分组'}, status=400)
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)}, status=500)


@csrf_exempt
@require_http_methods(["GET", "POST"])
def factors_view(request):
    if request.method == "GET":
        try:
            qs = CarbonFactor.objects.all()
            category = request.GET.get('category')
            q = request.GET.get('q')
            page = request.GET.get('page')
            page_size = request.GET.get('page_size')
            if category:
                qs = qs.filter(category=category)
            if q:
                qs = qs.filter(item_name__icontains=q)
            page_qs, meta = _paginate(qs.order_by('item_name'), page, page_size)
            data = [{
                'factor_id': f.factor_id,
                'item_name': f.item_name,
                'category': f.category,
                'emission_per_unit': str(f.emission_per_unit),
                'unit': f.unit
            } for f in page_qs]
            return JsonResponse({'success': True, 'data': {'items': data, 'meta': meta}})
        except Exception as e:
            return JsonResponse({'success': False, 'message': str(e)}, status=500)
    if request.method == "POST":
        try:
            if not request.user.is_authenticated:
                return JsonResponse({'success': False, 'message': '未登录'}, status=401)
            if not request.user.is_staff:
                return JsonResponse({'success': False, 'message': '无权限'}, status=403)
            payload = json.loads(request.body or '{}')
            item_name = payload.get('item_name')
            category = payload.get('category')
            emission_per_unit = payload.get('emission_per_unit')
            unit = payload.get('unit')
            if not item_name or not emission_per_unit or not unit:
                return JsonResponse({'success': False, 'message': '参数不足'}, status=400)
            if category:
                allowed_categories = [c[0] for c in CarbonFactor.CATEGORY_CHOICES]
                if category not in allowed_categories:
                    return JsonResponse({'success': False, 'message': '分类不在可选范围'}, status=400)
            obj, created = CarbonFactor.objects.get_or_create(
                item_name=item_name,
                defaults={
                    'category': category or '',
                    'emission_per_unit': Decimal(str(emission_per_unit)),
                    'unit': unit
                }
            )
            if not created:
                obj.category = category or obj.category
                obj.emission_per_unit = Decimal(str(emission_per_unit))
                obj.unit = unit
                obj.save()
            return JsonResponse({'success': True, 'message': '保存成功', 'data': {
                'factor_id': obj.factor_id,
                'item_name': obj.item_name,
                'category': obj.category,
                'emission_per_unit': str(obj.emission_per_unit),
                'unit': obj.unit
            }}, status=201 if created else 200)
        except json.JSONDecodeError:
            return JsonResponse({'success': False, 'message': '无效的JSON'}, status=400)
        except Exception as e:
            return JsonResponse({'success': False, 'message': str(e)}, status=500)

@csrf_exempt
@require_http_methods(["GET"])
@login_required
def trend_view(request):
    try:
        days = int(request.GET.get('days', 30))
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=days - 1)
        dates = []
        user_map = {}
        city_map = {}
        profile = request.user.profile
        city = profile.city or ''
        for i in range(days):
            d = start_date + timedelta(days=i)
            key = d.strftime('%Y-%m-%d')
            dates.append(key)
            user_map[key] = Decimal('0')
            city_map[key] = Decimal('0')
        qs_user = CarbonFootprint.objects.filter(user=request.user, record_date__gte=start_date, record_date__lte=end_date)
        for fp in qs_user:
            k = fp.record_date.strftime('%Y-%m-%d')
            user_map[k] = user_map.get(k, Decimal('0')) + fp.carbon_emission
        city_users = []
        if city:
            city_users = list(UserProfile.objects.filter(city=city).values_list('user_id', flat=True))
        if city_users:
            qs_city = CarbonFootprint.objects.filter(user_id__in=city_users, record_date__gte=start_date, record_date__lte=end_date)
            for fp in qs_city:
                k = fp.record_date.strftime('%Y-%m-%d')
                city_map[k] = city_map.get(k, Decimal('0')) + fp.carbon_emission
            denom = Decimal(str(len(set(city_users)))) if city_users else Decimal('1')
            city_series = [str((city_map[d] / denom).quantize(Decimal('0.01'))) for d in dates]
        else:
            city_series = [None for _ in dates]
        user_series = [str(user_map[d].quantize(Decimal('0.01'))) for d in dates]
        return JsonResponse({'success': True, 'data': {'city': city, 'dates': dates, 'user_emission': user_series, 'city_average_emission': city_series}})
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)}, status=500)

@csrf_exempt
@require_http_methods(["GET"])
@login_required
def report_view(request):
    try:
        scope = request.GET.get('scope', 'week')
        include_reduced = str(request.GET.get('include_reduced') or '').lower() in ['1','true','yes']
        end_date = timezone.now().date()
        days = 7 if scope == 'week' else 30
        start_date = end_date - timedelta(days=days - 1)
        qs = CarbonFootprint.objects.filter(user=request.user, record_date__gte=start_date, record_date__lte=end_date)
        total = qs.aggregate(total_emission=Sum('carbon_emission'))['total_emission'] or Decimal('0')
        total_reduced = Decimal('0')
        if include_reduced:
            for fp in qs:
                std = cache.get(f"carbon:std:{fp.footprint_id}") or {}
                rr = Decimal(str(std.get('reduced_co2') or '0') or '0')
                total_reduced += rr
        by_category = qs.values('category').annotate(sum_emission=Sum('carbon_emission')).order_by('-sum_emission')
        items = [{
            'footprint_id': fp.footprint_id,
            'category': fp.category,
            'quantity': fp.quantity,
            'carbon_emission': str(fp.carbon_emission),
            'record_date': fp.record_date.strftime('%Y-%m-%d')
        } for fp in qs.order_by('-record_date', '-footprint_id')]
        out = {
            'scope': scope,
            'start_date': start_date.strftime('%Y-%m-%d'),
            'end_date': end_date.strftime('%Y-%m-%d'),
            'total': str(total or Decimal('0')),
            'by_category': [{'category': g['category'], 'carbon_emission': str(g['sum_emission'] or Decimal('0'))} for g in by_category],
            'items': items
        }
        if include_reduced:
            out['total_reduced'] = str(total_reduced.quantize(Decimal('0.01')))
        return JsonResponse({'success': True, 'data': out})
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)}, status=500)

@csrf_exempt
@require_http_methods(["GET"])
@login_required
def recommendations_view(request):
    try:
        horizon_days = int(request.GET.get('horizon_days', 7))
        candidates = [
            {'title': '地铁通勤优化', 'desc': f'下周地铁通勤 3 次可减排 1.2 kg', 'reduction': Decimal('1.2'), 'roi': 0.85},
            {'title': '红肉消费调整', 'desc': f'每周减少 2 次红肉消费可减排 0.8 kg', 'reduction': Decimal('0.8'), 'roi': 0.70},
            {'title': '空调温度调节', 'desc': f'夏季空调调高 1℃可减排 0.5 kg', 'reduction': Decimal('0.5'), 'roi': 0.60},
            {'title': '公交替代驾车', 'desc': f'用公交替代驾车 2 次可减排 0.9 kg', 'reduction': Decimal('0.9'), 'roi': 0.65}
        ]
        top = sorted(candidates, key=lambda x: (Decimal(str(x['reduction'])), x['roi']), reverse=True)[:3]
        data = [{'title': t['title'], 'description': t['desc'], 'expected_reduction_kg': str(t['reduction']), 'roi_score': t['roi']} for t in top]
        return JsonResponse({'success': True, 'data': {'horizon_days': horizon_days, 'items': data}})
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)}, status=500)


def _safe_parse_json(text):
    try:
        return json.loads(text)
    except Exception:
        try:
            start = text.find('[')
            end = text.rfind(']')
            if start != -1 and end != -1:
                return json.loads(text[start:end+1])
            start = text.find('{')
            end = text.rfind('}')
            if start != -1 and end != -1:
                return json.loads(text[start:end+1])
        except Exception:
            return []
        return []

def build_calculation_prompt(text):
    tpl = (
        "解析并计算碳排与减排："
        "返回JSON数组，字段包括 item, unit, amount, actual_factor, baseline_factor, total_co2, reduced_co2, replaced_by。"
        "单位限定为 kg、km、杯、件、m3、g、米、斤、h、份、个；factor 为每单位 kg CO2e；"
        "reduced_co2 = (baseline_factor - actual_factor) * amount，若无替代关系则为 0；"
        "只返回JSON，不要解释。文本："
    )
    return tpl + str(text or "")

def parse_llm_json(content):
    c = str(content or "")
    parsed = _safe_parse_json(c)
    if isinstance(parsed, list):
        return parsed
    return []


@csrf_exempt
@require_http_methods(["GET"])
@login_required
def knowledge_view(request):
    try:
        horizon_days = int(request.GET.get('horizon_days', 7))
        force = str(request.GET.get('force', '')).lower() in ['1', 'true', 'yes']
        rate_min = int(os.environ.get('KNOWLEDGE_RATE_LIMIT_MINUTES', '10'))
        cache_ttl_min = int(os.environ.get('KNOWLEDGE_CACHE_TTL_MINUTES', '60'))
        k_data = f'knowledge:data:{request.user.id}:{horizon_days}'
        k_time = f'knowledge:last:{request.user.id}:{horizon_days}'
        if not force and cache.get(k_time):
            cached_items = cache.get(k_data) or []
            return JsonResponse({'success': True, 'data': {'items': cached_items, 'horizon_days': horizon_days, 'cached': True}})
        provider = (os.environ.get('AI_PROVIDER') or '').upper()
        appid = os.environ.get('SPARK_APPID') or os.environ.get('SPARK_APP_ID') or ''
        api_secret = os.environ.get('SPARK_API_SECRET') or ''
        api_key = os.environ.get('SPARK_API_KEY') or ''
        ws_url = os.environ.get('SPARK_WS_URL') or 'wss://spark-api.xf-yun.com/v1.1/chat'
        domain = os.environ.get('SPARK_DOMAIN') or 'lite'

        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=horizon_days - 1)
        qs = CarbonFootprint.objects.filter(user=request.user, record_date__gte=start_date, record_date__lte=end_date)
        total = qs.aggregate(total_emission=Sum('carbon_emission'))['total_emission'] or Decimal('0')
        by_category = qs.values('category').annotate(sum_emission=Sum('carbon_emission')).order_by('-sum_emission')
        context = {
            'horizon_days': horizon_days,
            'total_emission_kg': float(total or Decimal('0')),
            'by_category': [{ 'category': g['category'], 'emission_kg': float(g['sum_emission'] or Decimal('0')) } for g in by_category]
        }

        items = []
        prev = cache.get(k_data) or []
        prev_titles = ",".join([str(x.get('title','')) for x in (prev if isinstance(prev, list) else [])])
        seed = str(int(time.time()*1000) % 100000)
        prompt = (
            "你是一名低碳生活顾问。请基于下述用户碳足迹上下文生成不超过3条中文知识卡片，"
            "每条包含 title, summary, actionText, actionLink, expectedReductionKg, tags。"
            "避免与这些标题重复：" + prev_titles + "。随机种子：" + seed + "。"
            "要求可执行、无夸张词汇、以JSON数组输出。上下文：" + json.dumps(context, ensure_ascii=False)
        )

        # 使用 Spark Lite HTTP HMAC 鉴权方式
        if provider in ['', 'SPARKLITE']:
            try:
                spark_base = os.environ.get('SPARK_BASE_URL') or 'https://spark-api.xf-yun.com'
                spark_path = os.environ.get('SPARK_PATH') or '/v3.1/chat'
                appid2 = os.environ.get('SPARK_APPID') or os.environ.get('SPARK_APP_ID')
                key2 = os.environ.get('SPARK_API_KEY')
                secret2 = os.environ.get('SPARK_API_SECRET')
                if appid2 and key2 and secret2:
                    url = spark_base.rstrip('/') + spark_path
                    parsed_url = urllib.parse.urlparse(url)
                    host = parsed_url.netloc
                    date = formatdate(usegmt=True)
                    request_line = f"POST {parsed_url.path} HTTP/1.1"
                    signature_origin = f"host: {host}\ndate: {date}\n{request_line}"
                    signature_sha = hmac.new(secret2.encode('utf-8'), signature_origin.encode('utf-8'), hashlib.sha256).digest()
                    signature = base64.b64encode(signature_sha).decode('utf-8')
                    authorization = f'api_key="{key2}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature}"'

                    payload2 = {
                        "header": {"app_id": appid2, "uid": "knowledge"},
                        "parameter": {"chat": {"domain": domain, "temperature": 0.6, "max_tokens": 1200}},
                        "payload": {"message": {"text": [{"role": "user", "content": prompt}]}}
                    }
                    body2 = json.dumps(payload2).encode('utf-8')
                    req2 = urllib.request.Request(url, data=body2)
                    req2.add_header('Content-Type', 'application/json')
                    req2.add_header('Host', host)
                    req2.add_header('Date', date)
                    req2.add_header('Authorization', authorization)
                    try:
                        with urllib.request.urlopen(req2, timeout=10) as resp2:
                            raw2 = resp2.read().decode('utf-8')
                            data2 = json.loads(raw2)
                        rt = ""
                        segs = (((data2.get('payload') or {}).get('choices') or {}).get('text') or [])
                        for s in segs:
                            rt += s.get('content') or ''
                        parsed2 = _safe_parse_json(rt or '[]')
                        if isinstance(parsed2, list):
                            items = parsed2[:8]
                    except Exception:
                        open_url = 'https://spark-api-open.xf-yun.com/v1/chat/completions'
                        open_pwd = os.environ.get('SPARK_API_PASSWORD') or ''
                        headers = {
                            'Content-Type': 'application/json',
                            'Authorization': f'Bearer {open_pwd}'
                        }
                        data_open = {
                            'model': domain,
                            'messages': [
                                {"role": "system", "content": "仅返回JSON"},
                                {"role": "user", "content": prompt}
                            ],
                            'temperature': 0.6,
                            'max_tokens': 1200
                        }
                    req_open = urllib.request.Request(open_url, data=json.dumps(data_open).encode('utf-8'), headers=headers)
                    with urllib.request.urlopen(req_open, timeout=10) as resp_open:
                        ro = resp_open.read().decode('utf-8')
                        jd = json.loads(ro)
                    content = ((jd.get('choices') or [{}])[0].get('message') or {}).get('content') or '[]'
                    parsed = _safe_parse_json(content)
                    if isinstance(parsed, list):
                        items = parsed[:8]
                else:
                    open_url = 'https://spark-api-open.xf-yun.com/v1/chat/completions'
                    open_pwd = os.environ.get('SPARK_API_PASSWORD') or ''
                    headers = {
                        'Content-Type': 'application/json',
                        'Authorization': f'Bearer {open_pwd}'
                    }
                    data_open = {
                        'model': domain,
                        'messages': [
                            {"role": "system", "content": "仅返回JSON"},
                            {"role": "user", "content": prompt}
                        ],
                        'temperature': 0.6,
                        'max_tokens': 1200
                    }
                    req_open = urllib.request.Request(open_url, data=json.dumps(data_open).encode('utf-8'), headers=headers)
                    with urllib.request.urlopen(req_open, timeout=10) as resp_open:
                        ro = resp_open.read().decode('utf-8')
                        jd = json.loads(ro)
                    content = ((jd.get('choices') or [{}])[0].get('message') or {}).get('content') or '[]'
                    parsed = _safe_parse_json(content)
                    if isinstance(parsed, list):
                        items = parsed[:8]
            except Exception:
                items = []
        # 去重与质量过滤、按相关性排序
        prev_set = set([str(x.get('title') or '').strip().lower() for x in (prev if isinstance(prev, list) else [])])
        def _quality_filter(arr):
            seen = set()
            filtered = []
            for x in (arr or []):
                t = str(x.get('title') or '').strip()
                s = str(x.get('summary') or '').strip()
                if not t or not s:
                    continue
                tl = t.lower()
                if tl in seen:
                    continue
                if tl in prev_set:
                    continue
                seen.add(tl)
                filtered.append(x)
            return filtered
        def _relevance_score(x):
            title = (x.get('title') or '').lower()
            score = 0.0
            for g in context.get('by_category') or []:
                c = (g.get('category') or '').lower()
                w = float(g.get('emission_kg') or 0)
                if c and c in title:
                    score += w
            score += float(x.get('expectedReductionKg') or 0) * 1.5
            return score
        items = _quality_filter(items)
        items = sorted(items, key=_relevance_score, reverse=True)
        if len(items) < 5:
            key = os.environ.get('DEEPSEEK_API_KEY') or os.environ.get('AI_API_KEY')
            base = os.environ.get('DEEPSEEK_BASE_URL') or os.environ.get('AI_BASE_URL') or 'https://api.deepseek.com'
            model = os.environ.get('DEEPSEEK_MODEL') or os.environ.get('AI_MODEL') or 'deepseek-chat'
            if key:
                endpoint = base.rstrip('/') + '/v1/chat/completions'
                payload = {
                    "model": model,
                    "messages": [
                        {"role": "system", "content": "仅返回JSON"},
                        {"role": "user", "content": prompt}
                    ],
                    "temperature": 0.7,
                    "top_p": 0.9
                }
                req = urllib.request.Request(endpoint, data=body)
                req.add_header('Content-Type', 'application/json')
                req.add_header('Authorization', f'Bearer {key}')
                with urllib.request.urlopen(req, timeout=10) as resp:
                    raw = resp.read().decode('utf-8')
                    data = json.loads(raw)
                content = ((data.get('choices') or [{}])[0].get('message') or {}).get('content') or '[]'
                if isinstance(parsed, list):
                    items = _quality_filter(parsed)[:8]

        if not items:
            top = (context.get('by_category') or [])[:3]
            if not top:
                top = [{'category': '居家', 'emission_kg': 2.0}, {'category': '交通', 'emission_kg': 2.0}, {'category': '餐饮', 'emission_kg': 2.0}]
            rnd = random.Random(int(seed))
            def mk_variants(c, e):
                base = max(0.2, min(2.0, float(e or 0) * 0.15))
                jitter = (0.9 + 0.2 * rnd.random())
                r = round(base * jitter, 2)
                if c == '餐饮':
                    variants = [
                        {"title": "少红肉多豆类", "summary": "减少红肉摄入，增加豆类与蔬菜，当季本地食材。", "tags": ["餐饮","替换"]},
                        {"title": "一周素食日", "summary": "设定每周1天素食，控制份量，减少食物浪费。", "tags": ["餐饮","素食"]},
                        {"title": "减少外卖一次性餐具", "summary": "自备餐具，减少一次性用品，优先堂食或自煮。", "tags": ["餐饮","减塑"]}
                    ]
                elif c == '交通':
                    variants = [
                        {"title": "步行/骑行替代短途驾车", "summary": "短途优先步行或骑行，结合地铁通勤，规划路线。", "tags": ["交通","替代"]},
                        {"title": "合乘/拼车", "summary": "与同事或邻居合乘，减少单人驾车出行。", "tags": ["交通","合乘"]},
                        {"title": "错峰与路线优化", "summary": "避开拥堵时段，优化路线以减少怠速。", "tags": ["交通","优化"]}
                    ]
                else:
                    variants = [
                        {"title": "空调调高1℃", "summary": "提高设定温度1℃，使用节能模式，关闭无人房间空调。", "tags": ["居家","节能"]},
                        {"title": "关闭待机与更换LED", "summary": "关闭待机设备，更换LED照明，减少无效耗电。", "tags": ["居家","用电"]},
                        {"title": "热水器与冰箱设置优化", "summary": "热水器温度合理设置，冰箱分类存放提高效率。", "tags": ["居家","设置"]}
                    ]
                rnd.shuffle(variants)
                out = []
                for v in variants:
                    t = str(v.get('title') or '')
                    if t.lower() in prev_set:
                        continue
                    out.append({"title": v["title"], "summary": v["summary"], "actionText": "", "actionLink": "", "expectedReductionKg": r, "tags": v["tags"]})

        cache.set(k_time, True, rate_min * 60)

        from .models import KnowledgeLog
        KnowledgeLog.objects.create(user=request.user, horizon_days=horizon_days, prompt_text=prompt, items_json=items)

        return JsonResponse({'success': True, 'data': {'items': items[:8], 'horizon_days': horizon_days}})
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)}, status=500)


@csrf_exempt
@require_http_methods(["GET"])
@login_required
def knowledge_health_view(request):
    try:
        appid = os.environ.get('SPARK_APPID') or os.environ.get('SPARK_APP_ID') or ''
        api_secret = os.environ.get('SPARK_API_SECRET') or ''
        api_key = os.environ.get('SPARK_API_KEY') or ''
        spark_base = os.environ.get('SPARK_BASE_URL') or 'https://spark-api.xf-yun.com'
        spark_path = os.environ.get('SPARK_PATH') or '/v3.1/chat'
        domain = os.environ.get('SPARK_DOMAIN') or 'lite'
        env_present = {"appid": bool(appid), "api_key": bool(api_key), "api_secret": bool(api_secret)}
        if appid and api_secret and api_key:
            url = spark_base.rstrip('/') + spark_path
            parsed_url = urllib.parse.urlparse(url)
            host = parsed_url.netloc
            date = formatdate(usegmt=True)
            request_line = f"POST {parsed_url.path} HTTP/1.1"
            signature_origin = f"host: {host}\ndate: {date}\n{request_line}"
            signature_sha = hmac.new(api_secret.encode('utf-8'), signature_origin.encode('utf-8'), hashlib.sha256).digest()
            signature = base64.b64encode(signature_sha).decode('utf-8')
            authorization = f'api_key="{api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature}"'
            payload = {
                "header": {"app_id": appid, "uid": "health"},
                "parameter": {"chat": {"domain": domain, "temperature": 0, "max_tokens": 32}},
                "payload": {"message": {"text": [{"role": "user", "content": "健康检查：仅回复OK"}]}}
            }
            body = json.dumps(payload).encode('utf-8')
            req = urllib.request.Request(url, data=body)
            req.add_header('Content-Type', 'application/json')
            req.add_header('Host', host)
            req.add_header('Date', date)
            req.add_header('Authorization', authorization)
            t0 = time.time()
            with urllib.request.urlopen(req, timeout=8) as resp:
                raw = resp.read().decode('utf-8')
                data = json.loads(raw)
            segs = (((data.get('payload') or {}).get('choices') or {}).get('text') or [])
            result_text = ""
            for s in segs:
                result_text += (s.get('content') or '')
            ok = isinstance(result_text, str) and ('OK' in result_text.upper())
            latency_ms = int((time.time() - t0) * 1000)
            return JsonResponse({'success': True, 'data': {'ok': ok, 'latency_ms': latency_ms, 'env': env_present}})
    except Exception as e:
        return JsonResponse({'success': True, 'data': {'ok': False, 'error': str(e)}}, status=200)
@csrf_exempt
@require_http_methods(["GET"])
@login_required
def reduction_statistics_view(request):
    try:
        start_date = request.GET.get('start_date')
        end_date = request.GET.get('end_date')
        qs = CarbonFootprint.objects.filter(user=request.user)
        if start_date:
            qs = qs.filter(record_date__gte=start_date)
        if end_date:
            qs = qs.filter(record_date__lte=end_date)
        total_reduced = Decimal('0')
        by_category = {}
        for fp in qs:
            std = cache.get(f"carbon:std:{fp.footprint_id}") or {}
            rr = Decimal(str(std.get('reduced_co2') or '0') or '0')
            total_reduced += rr
            cat = fp.category or '其他'
            by_category[cat] = by_category.get(cat, Decimal('0')) + rr
        data = {
            'total_reduced': str(total_reduced.quantize(Decimal('0.01'))),
            'by_category': [{'category': k, 'reduced_co2': str(v.quantize(Decimal('0.01')))} for k, v in sorted(by_category.items(), key=lambda x: x[1], reverse=True)]
        }
        return JsonResponse({'success': True, 'data': data})
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)}, status=500)
